home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / admin / shadow-9.tar / shadow-9 / shadow-960129 / shadowio.c < prev    next >
C/C++ Source or Header  |  1995-12-17  |  14KB  |  664 lines

  1. /*
  2.  * Copyright 1990 - 1994, John F. Haugh II
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice, this list of conditions and the following disclaimer in the
  12.  *    documentation and/or other materials provided with the distribution.
  13.  * 3. All advertising materials mentioning features or use of this software
  14.  *    must display the following acknowledgement:
  15.  *    This product includes software developed by John F. Haugh, II
  16.  *      and other contributors.
  17.  * 4. Neither the name of John F. Haugh, II nor the names of its contributors
  18.  *    may be used to endorse or promote products derived from this software
  19.  *    without specific prior written permission.
  20.  *
  21.  * THIS SOFTWARE IS PROVIDED BY JOHN HAUGH AND CONTRIBUTORS ``AS IS'' AND
  22.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24.  * ARE DISCLAIMED.  IN NO EVENT SHALL JOHN HAUGH OR CONTRIBUTORS BE LIABLE
  25.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31.  * SUCH DAMAGE.
  32.  *
  33.  *    This file implements a transaction oriented password database
  34.  *    library.  The password file is updated one entry at a time.
  35.  *    After each transaction the file must be logically closed and
  36.  *    transferred to the existing password file.  The sequence of
  37.  *    events is
  38.  *
  39.  *    spw_lock            -- lock shadow file
  40.  *    spw_open            -- logically open shadow file
  41.  *    while transaction to process
  42.  *        spw_(locate,update,remove) -- perform transaction
  43.  *    done
  44.  *    spw_close            -- commit transactions
  45.  *    spw_unlock            -- remove shadow lock
  46.  */
  47.  
  48. #include <sys/types.h>
  49. #include <sys/stat.h>
  50. #include <fcntl.h>
  51. #include <errno.h>
  52. #include <stdio.h>
  53. #include <signal.h>
  54. #include "config.h"
  55. #include "prototypes.h"
  56. #include "defines.h"
  57.  
  58. #ifdef    SHADOWPWD    /*{*/
  59. #include "shadow.h"
  60.  
  61. #ifndef    lint
  62. static char rcsid[] = "$Id: shadowio.c,v 1.2 1995/12/16 23:50:23 marekm Exp $";
  63. #endif
  64.  
  65. static    int    islocked;
  66. static    int    isopen;
  67. static    int    open_modes;
  68. static    FILE    *spwfp;
  69.  
  70. struct    spw_file_entry {
  71.     char    *spwf_line;
  72.     int    spwf_changed;
  73.     struct    spwd    *spwf_entry;
  74.     struct    spw_file_entry *spwf_next;
  75. };
  76.  
  77. struct    spw_file_entry    *__spwf_head;
  78. static    struct    spw_file_entry    *spwf_tail;
  79. static    struct    spw_file_entry    *spwf_cursor;
  80. int    __sp_changed;
  81. static    int    lock_pid;
  82.  
  83. #define    SPW_LOCK    "/etc/shadow.lock"
  84. #define    SPW_TEMP    "/etc/spwd.%d"
  85.  
  86. static    char    spw_filename[BUFSIZ] = SHADOW_FILE;
  87.  
  88. extern    char    *strdup();
  89. extern    char    *malloc();
  90. extern    struct    spwd    *sgetspent();
  91.  
  92. /*
  93.  * spw_dup - duplicate a shadow file entry
  94.  *
  95.  *    spw_dup() accepts a pointer to a shadow file entry and
  96.  *    returns a pointer to a shadow file entry in allocated
  97.  *    memory.
  98.  */
  99.  
  100. static struct spwd *
  101. spw_dup (spwd)
  102. struct    spwd    *spwd;
  103. {
  104.     struct    spwd    *spw;
  105.  
  106.     if (! (spw = (struct spwd *) malloc (sizeof *spw)))
  107.         return 0;
  108.  
  109.     *spw = *spwd;
  110.     if ((spw->sp_namp = strdup (spwd->sp_namp)) == 0 ||
  111.             (spw->sp_pwdp = strdup (spwd->sp_pwdp)) == 0)
  112.         return 0;
  113.  
  114.     return spw;
  115. }
  116.  
  117. /*
  118.  * spw_free - free a dynamically allocated shadow file entry
  119.  *
  120.  *    spw_free() frees up the memory which was allocated for the
  121.  *    pointed to entry.
  122.  */
  123.  
  124. static void
  125. spw_free (spwd)
  126. struct    spwd    *spwd;
  127. {
  128.     free (spwd->sp_namp);
  129.     free (spwd->sp_pwdp);
  130. }
  131.  
  132. /*
  133.  * spw_name - change the name of the shadow password file
  134.  */
  135.  
  136. int
  137. spw_name (name)
  138. char    *name;
  139. {
  140.     if (isopen || strlen (name) > (BUFSIZ-10))
  141.         return -1;
  142.  
  143.     strcpy (spw_filename, name);
  144.     return 0;
  145. }
  146.  
  147. /*
  148.  * spw_lock - lock a password file
  149.  *
  150.  *    spw_lock() encapsulates the lock operation.  it returns
  151.  *    TRUE or FALSE depending on the password file being
  152.  *    properly locked.  the lock is set by creating a semaphore
  153.  *    file, SPW_LOCK.
  154.  */
  155.  
  156. int
  157. spw_lock ()
  158. {
  159.     int    fd;
  160.     int    pid;
  161.     int    len;
  162.     char    file[BUFSIZ];
  163.     char    lock[BUFSIZ];
  164.     char    buf[32];
  165.     struct    stat    sb;
  166.  
  167.     if (islocked)
  168.         return 1;
  169.  
  170.     if (strcmp (spw_filename, SHADOW_FILE) != 0) {
  171.         sprintf (file, "%s.%d", spw_filename, lock_pid = getpid ());
  172.         sprintf (lock, "%s.lock", spw_filename);
  173.     } else {
  174.         sprintf (file, SPW_TEMP, lock_pid = getpid ());
  175.         strcpy (lock, SPW_LOCK);
  176.     }
  177.  
  178.     /*
  179.      * Create a lock file which can be switched into place
  180.      */
  181.  
  182.     if ((fd = open (file, O_CREAT|O_EXCL|O_WRONLY, 0600)) == -1)
  183.         return 0;
  184.  
  185.     sprintf (buf, "%d", lock_pid);
  186.     if (write (fd, buf, strlen (buf) + 1) != strlen (buf) + 1) {
  187.         (void) close (fd);
  188.         (void) unlink (file);
  189.         return 0;
  190.     }
  191.     close (fd);
  192.  
  193.     /*
  194.      * Simple case first -
  195.      *    Link fails (in a sane environment ...) if the target
  196.      *    exists already.  So we try to switch in a new lock
  197.      *    file.  If that succeeds, we assume we have the only
  198.      *    valid lock.  Needs work for NFS where this assumption
  199.      *    may not hold.  The simple hack is to check the link
  200.      *    count on the source file, which should be 2 iff the
  201.      *    link =really= worked.
  202.      */
  203.  
  204.     if (link (file, lock) == 0) {
  205.         if (stat (file, &sb) != 0)
  206.             return 0;
  207.  
  208.         if (sb.st_nlink != 2)
  209.             return 0;
  210.  
  211.         (void) unlink (file);
  212.         islocked = 1;
  213.         return 1;
  214.     }
  215.  
  216.     /*
  217.      * Invalid lock test -
  218.      *    Open the lock file and see if the lock is valid.
  219.      *    The PID of the lock file is checked, and if the PID
  220.      *    is not valid, the lock file is removed.  If the unlink
  221.      *    of the lock file fails, it should mean that someone
  222.      *    else is executing this code.  They will get success,
  223.      *    and we will fail.
  224.      */
  225.  
  226.     if ((fd = open (lock, O_RDWR)) == -1 ||
  227.             (len = read (fd, buf, BUFSIZ)) <= 0) {
  228.         errno = EINVAL;
  229.         return 0;
  230.     }
  231.     buf[len] = '\0';
  232.     if ((pid = strtol (buf, (char **) 0, 10)) == 0) {
  233.         errno = EINVAL;
  234.         return 0;
  235.     }
  236.     if (kill (pid, 0) == 0)  {
  237.         errno = EEXIST;
  238.         return 0;
  239.     }
  240.     if (unlink (lock)) {
  241.         (void) close (fd);
  242.         (void) unlink (file);
  243.  
  244.         return 0;
  245.     }
  246.  
  247.     /*
  248.      * Re-try lock -
  249.      *    The invalid lock has now been removed and I should
  250.      *    be able to acquire a lock for myself just fine.  If
  251.      *    this fails there will be no retry.  The link count
  252.      *    test here makes certain someone executing the previous
  253.      *    block of code didn't just remove the lock we just
  254.      *    linked to.
  255.      */
  256.  
  257.     if (link (file, lock) == 0) {
  258.         if (stat (file, &sb) != 0)
  259.             return 0;
  260.  
  261.         if (sb.st_nlink != 2)
  262.             return 0;
  263.  
  264.         (void) unlink (file);
  265.         islocked = 1;
  266.         return 1;
  267.     }
  268.     (void) unlink (file);
  269.     return 0;
  270. }
  271.  
  272. /*
  273.  * spw_unlock - logically unlock a shadow file
  274.  *
  275.  *    spw_unlock() removes the lock which was set by an earlier
  276.  *    invocation of spw_lock().
  277.  */
  278.  
  279. int
  280. spw_unlock ()
  281. {
  282.     char    lock[BUFSIZ];
  283.  
  284.     if (isopen) {
  285.         open_modes = O_RDONLY;
  286.         if (! spw_close ())
  287.             return 0;
  288.     }
  289.       if (islocked) {
  290.           islocked = 0;
  291.         if (lock_pid != getpid ())
  292.             return 0;
  293.  
  294.         strcpy (lock, spw_filename);
  295.         strcat (lock, ".lock");
  296.         (void) unlink (lock);
  297.         return 1;
  298.     }
  299.     return 0;
  300. }
  301.  
  302. /*
  303.  * spw_open - open a password file
  304.  *
  305.  *    spw_open() encapsulates the open operation.  it returns
  306.  *    TRUE or FALSE depending on the shadow file being
  307.  *    properly opened.
  308.  */
  309.  
  310. int
  311. spw_open (mode)
  312. int    mode;
  313. {
  314.     char    buf[BUFSIZ];
  315.     char    *cp;
  316.     struct    spw_file_entry    *spwf;
  317.     struct    spwd    *spwd;
  318.  
  319.     if (isopen || (mode != O_RDONLY && mode != O_RDWR))
  320.         return 0;
  321.  
  322.     if (mode != O_RDONLY && ! islocked &&
  323.             strcmp (spw_filename, SHADOW_FILE) == 0)
  324.         return 0;
  325.  
  326.     if ((spwfp = fopen (spw_filename, mode == O_RDONLY ? "r":"r+")) == 0)
  327.         return 0;
  328.  
  329.     __spwf_head = spwf_tail = spwf_cursor = 0;
  330.     __sp_changed = 0;
  331.  
  332.     while (fgets (buf, sizeof buf, spwfp) != (char *) 0) {
  333.         if ((cp = strrchr (buf, '\n')))
  334.             *cp = '\0';
  335.  
  336.         if (! (spwf = (struct spw_file_entry *) malloc (sizeof *spwf)))
  337.             return 0;
  338.  
  339.         spwf->spwf_changed = 0;
  340.         if (! (spwf->spwf_line = strdup (buf)))
  341.             return 0;
  342.         if ((spwd = sgetspent (buf)) && ! (spwd = spw_dup (spwd)))
  343.             return 0;
  344.  
  345.         spwf->spwf_entry = spwd;
  346.  
  347.         if (__spwf_head == 0) {
  348.             __spwf_head = spwf_tail = spwf;
  349.             spwf->spwf_next = 0;
  350.         } else {
  351.             spwf_tail->spwf_next = spwf;
  352.             spwf->spwf_next = 0;
  353.             spwf_tail = spwf;
  354.         }
  355.     }
  356.     isopen++;
  357.     open_modes = mode;
  358.  
  359.     return 1;
  360. }
  361.  
  362. /*
  363.  * spw_close - close the password file
  364.  *
  365.  *    spw_close() outputs any modified password file entries and
  366.  *    frees any allocated memory.
  367.  */
  368.  
  369. int
  370. spw_close ()
  371. {
  372.     char    backup[BUFSIZ];
  373.     char    newfile[BUFSIZ];
  374.     int    mask;
  375.     int    c;
  376.     int    errors = 0;
  377.     FILE    *bkfp;
  378.     struct    spw_file_entry *spwf;
  379.     struct    stat    sb;
  380.  
  381.     if (! isopen) {
  382.         errno = EINVAL;
  383.         return 0;
  384.     }
  385.     if (islocked && lock_pid != getpid ()) {
  386.         isopen = 0;
  387.         islocked = 0;
  388.         errno = EACCES;
  389.         return 0;
  390.     }
  391.     strcpy (backup, spw_filename);
  392.     strcat (backup, "-");
  393.     strcpy (newfile, spw_filename);
  394.     strcat (newfile, "+");
  395.  
  396.     /*
  397.      * Create a backup copy of the shadow password file
  398.      */
  399.  
  400.     if (open_modes == O_RDWR && __sp_changed) {
  401.  
  402.         /*
  403.          * POLICY: /etc/shadow
  404.          * Any backup copy of the password file shall have the
  405.          * same protections as the original.
  406.          */
  407.  
  408.         mask = umask (0777);
  409.         (void) unlink (backup);
  410.         if ((bkfp = fopen (backup, "w")) == 0) {
  411.             umask (mask);
  412.             return 0;
  413.         }
  414.         umask (mask);
  415.         fstat (fileno (spwfp), &sb);
  416.         (void) chmod (backup, sb.st_mode);
  417.         (void) chown (backup, sb.st_uid, sb.st_gid);
  418.  
  419.         rewind (spwfp);
  420.         while ((c = getc (spwfp)) != EOF) {
  421.             if (putc (c, bkfp) == EOF) {
  422.                 fclose (bkfp);
  423.                 return 0;
  424.             }
  425.         }
  426.         if (fclose (bkfp))
  427.             return 0;
  428.  
  429.         isopen = 0;
  430.         (void) fclose (spwfp);
  431.  
  432.         /*
  433.          * POLICY: /etc/shadow
  434.          * The shadow password file shall allow write access to
  435.          * privileged users only.
  436.          *
  437.          * The shadow password file is opened with no access
  438.          * permissions to any user.  This allows the file to be
  439.          * changed to root ownership and then made readable by the
  440.          * owner without ever giving any unprivileged user write
  441.          * access.
  442.          */
  443.  
  444.         mask = umask (0777);
  445.         if (! (spwfp = fopen (newfile, "w"))) {
  446.             umask (mask);
  447.             return 0;
  448.         }
  449.         umask (mask);
  450.         if (chown (newfile, (UID_T) 0, (GID_T) 0) ||
  451.                 chmod (newfile, 0400))
  452.             return 0;
  453.  
  454.         /*
  455.          * Check each member in the list and write out any elements
  456.          * that have been changed.
  457.          */
  458.  
  459.         for (spwf = __spwf_head;errors == 0 && spwf;
  460.                         spwf = spwf->spwf_next) {
  461.             if (spwf->spwf_changed) {
  462.                 if (putspent (spwf->spwf_entry, spwfp))
  463.                     errors++;
  464.             } else {
  465.                 if (fputs (spwf->spwf_line, spwfp) == EOF)
  466.                     errors++;
  467.                 if (putc ('\n', spwfp) == EOF)
  468.                     errors++;
  469.             }
  470.         }
  471.         if (fflush (spwfp))
  472.             errors++;
  473.         if (fclose (spwfp))
  474.             errors++;
  475.  
  476.         if (errors) {
  477.             unlink (newfile);
  478.             return 0;
  479.         }
  480.  
  481.         /*
  482.          * POLICY: /etc/shadow
  483.          * The shadow password file shall be consistent at all
  484.          * times.
  485.          *
  486.          * The new shadow password file is moved into place only
  487.          * after determining that the file was created without any
  488.          * errors occuring.
  489.          */
  490.  
  491.         if (rename (newfile, spw_filename))
  492.             return 0;
  493.     } else
  494.         /*
  495.          * Just close the file -- there was nothing to change
  496.          */
  497.  
  498.         fclose (spwfp);
  499.  
  500.     spwfp = 0;
  501.  
  502.     /*
  503.      * Free up all of the memory in the linked list.
  504.      */
  505.  
  506.     while (__spwf_head != 0) {
  507.         spwf = __spwf_head;
  508.         __spwf_head = spwf->spwf_next;
  509.  
  510.         if (spwf->spwf_entry) {
  511.             spw_free (spwf->spwf_entry);
  512.             free (spwf->spwf_entry);
  513.         }
  514.         if (spwf->spwf_line)
  515.             free (spwf->spwf_line);
  516.  
  517.         free (spwf);
  518.     }
  519.     spwf_tail = 0;
  520.     isopen = 0;
  521.     return 1;
  522. }
  523.  
  524. int
  525. spw_update (spwd)
  526. struct    spwd    *spwd;
  527. {
  528.     struct    spw_file_entry    *spwf;
  529.     struct    spwd    *nspwd;
  530.  
  531.     if (! isopen || open_modes == O_RDONLY) {
  532.         errno = EINVAL;
  533.         return 0;
  534.     }
  535.     for (spwf = __spwf_head;spwf != 0;spwf = spwf->spwf_next) {
  536.         if (spwf->spwf_entry == 0)
  537.             continue;
  538.  
  539.         if (strcmp (spwd->sp_namp, spwf->spwf_entry->sp_namp) != 0)
  540.             continue;
  541.  
  542.         if (! (nspwd = spw_dup (spwd)))
  543.             return 0;
  544.         else {
  545.             spw_free (spwf->spwf_entry);
  546.             *(spwf->spwf_entry) = *nspwd;
  547.         }
  548.         spwf->spwf_changed = 1;
  549.         spwf_cursor = spwf;
  550.         return __sp_changed = 1;
  551.     }
  552.     spwf = (struct spw_file_entry *) malloc (sizeof *spwf);
  553.     if (!spwf)
  554.         return 0;
  555.     if (! (spwf->spwf_entry = spw_dup (spwd)))
  556.         return 0;
  557.  
  558.     spwf->spwf_changed = 1;
  559.     spwf->spwf_next = 0;
  560.     spwf->spwf_line = 0;
  561.  
  562.     if (spwf_tail)
  563.         spwf_tail->spwf_next = spwf;
  564.  
  565.     if (! __spwf_head)
  566.         __spwf_head = spwf;
  567.  
  568.     spwf_tail = spwf;
  569.  
  570.     return __sp_changed = 1;
  571. }
  572.  
  573. int
  574. spw_remove (name)
  575. char    *name;
  576. {
  577.     struct    spw_file_entry    *spwf;
  578.     struct    spw_file_entry    *ospwf;
  579.  
  580.     if (! isopen || open_modes == O_RDONLY) {
  581.         errno = EINVAL;
  582.         return 0;
  583.     }
  584.     for (ospwf = 0, spwf = __spwf_head;spwf != 0;
  585.             ospwf = spwf, spwf = spwf->spwf_next) {
  586.         if (! spwf->spwf_entry)
  587.             continue;
  588.  
  589.         if (strcmp (name, spwf->spwf_entry->sp_namp) != 0)
  590.             continue;
  591.  
  592.         if (spwf == spwf_cursor)
  593.             spwf_cursor = ospwf;
  594.  
  595.         if (ospwf != 0)
  596.             ospwf->spwf_next = spwf->spwf_next;
  597.         else
  598.             __spwf_head = spwf->spwf_next;
  599.  
  600.         if (spwf == spwf_tail)
  601.             spwf_tail = ospwf;
  602.  
  603.         return __sp_changed = 1;
  604.     }
  605.     errno = ENOENT;
  606.     return 0;
  607. }
  608.  
  609. struct spwd *
  610. spw_locate (name)
  611. char    *name;
  612. {
  613.     struct    spw_file_entry    *spwf;
  614.  
  615.     if (! isopen) {
  616.         errno = EINVAL;
  617.         return 0;
  618.     }
  619.     for (spwf = __spwf_head;spwf != 0;spwf = spwf->spwf_next) {
  620.         if (spwf->spwf_entry == 0)
  621.             continue;
  622.  
  623.         if (strcmp (name, spwf->spwf_entry->sp_namp) == 0) {
  624.             spwf_cursor = spwf;
  625.             return spwf->spwf_entry;
  626.         }
  627.     }
  628.     errno = ENOENT;
  629.     return 0;
  630. }
  631.  
  632. int
  633. spw_rewind ()
  634. {
  635.     if (! isopen) {
  636.         errno = EINVAL;
  637.         return 0;
  638.     }
  639.     spwf_cursor = 0;
  640.     return 1;
  641. }
  642.  
  643. struct spwd *
  644. spw_next ()
  645. {
  646.     if (! isopen) {
  647.         errno = EINVAL;
  648.         return 0;
  649.     }
  650.     if (spwf_cursor == 0)
  651.         spwf_cursor = __spwf_head;
  652.     else
  653.         spwf_cursor = spwf_cursor->spwf_next;
  654.  
  655.     while (spwf_cursor) {
  656.         if (spwf_cursor->spwf_entry)
  657.             return spwf_cursor->spwf_entry;
  658.  
  659.         spwf_cursor = spwf_cursor->spwf_next;
  660.     }
  661.     return 0;
  662. }
  663. #endif    /*}*/
  664.